# -*- coding: utf-8 -*-

'''
projecto- classe que permite testar e comparar o desempenho de 
um algoritmo de ordenação com n elementos de entrada
autor- Ana Isabel Marques 6052
data de criação do módulo- 27/05/2013
'''
from insertion_sort import *
from bubble_sort import *
from heapsort import *
from merge_sort import *
from quicksort import *

from random import uniform
from time import clock
from calculos import *
import time
import math


class EstatisticasGrafico:
    E = []

    '''
    Construtor
    @param numero de execuções - numero de execuções
    @tipoOrd - tipo de ordenação (algoritmo de ordenação escolhido pelo utilizador)
    '''
    def __init__(self, numeroExecucoes, tipoOrd):
        self.A = []
        self.numeroExecucoes = numeroExecucoes
        self.tipoOrd = tipoOrd
        self.TEMPOS = []
        self.LISTA_INFORMACAO_GRAFICO = []
        self.T = []

        pass
	       
    '''
    Com base nos tempos de execução realizados ao algoritmo de 
    ordenação, são determinados: a média, a variância, o devio 
    padrão e o erro amostral.
    
    Retorna a lista que contém n (o tamanho de entrada da lista) e
    os valores realizados ao estudo experimental do algoritmo.
    '''
    def calcularEstatisticas(self):
        tempoTotal = 0
        Z = [20] + range(50, 1050, 50)
        
        for n in Z:
            self.A = [ uniform(0.0,1.0) for k in xrange(n)]

            for k in range(self.numeroExecucoes):
                t1 = clock()
                self.tipoOrdenacao()
                t2 = clock()
                tempoTotal = t2-t1
                self.TEMPOS.append(t2-t1)
                pass
                    
            calculo = Calculos()
            media = calculo.calculaMedia(self.TEMPOS)
            var = calculo.calculaVariancia(self.TEMPOS, media)
            desvioPadrao = calculo.calculaDesvioPadrao(var)                      
            erroAmostral = calculo.calculaErroAmostral(var, n)

            self.E.append((n, tempoTotal, media, var, desvioPadrao, erroAmostral))
                 
            pass
                 
        return self.E


    '''
    Retorna uma lista com os valores destinados ao desenho do gráfico.
    A lista contém os valores baseado no estudo computacional realizado 
    a um algoritmo.
    
    O gráfico irá mostrar a complexidade computacional de um algoritmo
    de uma forma geral: - previsão teórica
            - complexidade para o pior caso
            - complexidade para o caso médio
            - complexidade para o melhor caso
    '''
    def informacaoGraficoPrevisaoTeorica(self):
                        
        calculo = Calculos()
        self.E = self.calcularEstatisticas()

        X = [n for n, tempo, media, var, desvioPadrao, erro_amostral in self.E]
        Y = [media for n, tempo, media, var, desvioPadrao, erro_amostral in self.E]
        
        ct=19e6

        self.LISTA_INFORMACAO_GRAFICO.append((X, Y, "-", "magenta", "o", "resultado experimental"))
        
        if(self.tipoOrd == 0):
            Z = [calculo.complexidadeTemporalLogaritmica(n)/ct for n, tempo, media, var, desvioPadrao, erro_amostral in self.E]
            self.LISTA_INFORMACAO_GRAFICO.append((X, Z, "-", "blue", "o", u"previsão teórica: pior caso/caso médio"))

            Z = [calculo.complexidadeTemporalQuadratica(n)/ct for n, tempo, media, var, desvioPadrao, erro_amostral in self.E]
            self.LISTA_INFORMACAO_GRAFICO.append((X, Z, "-", "green", "o", u"previsão teórica: melhor caso"))
            pass
        
        elif(self.tipoOrd == 1):
            Z = [calculo.complexidadeTemporalQuadratica(n)/ct for n, tempo, media, var, desvioPadrao, erro_amostral in self.E]
            self.LISTA_INFORMACAO_GRAFICO.append((X, Z, "-", "blue", "o", u"previsão teórica: pior caso/caso médio"))

            Z = [calculo.complexidadeTemporalLinear(n)/ct for n, tempo, media, var, desvioPadrao, erro_amostral in self.E]
            self.LISTA_INFORMACAO_GRAFICO.append((X, Z, "-", "green", "o", u"previsão teórica: melhor caso"))
            pass
			
        elif(self.tipoOrd == 2 or self.tipoOrd == 3):
                Z = [calculo.complexidadeTemporalLogaritmica(n)/ct for n, tempo, media, var, desvioPadrao, erro_amostral in self.E]
                self.LISTA_INFORMACAO_GRAFICO.append((X, Z, "-", "orange", "o", u"previsão teórica: pior caso/caso médio/melhor caso"))
                pass
		
        else:
                Z = [calculo.complexidadeTemporalQuadratica(n)/ct for n, tempo, media, var, desvioPadrao, erro_amostral in self.E]
                self.LISTA_INFORMACAO_GRAFICO.append((X, Z, "-", "blue", "o", u"previsão teórica: pior caso"))
                Z = [calculo.complexidadeTemporalLogaritmica(n)/ct for n, tempo,media, var, desvioPadrao, erro_amostral in self.E]
                self.LISTA_INFORMACAO_GRAFICO.append((X, Z, "-", "green", "o", u"previsão teórica: caso médio/melhor caso"))
                pass
     
     
        return self.LISTA_INFORMACAO_GRAFICO

    '''
    Retorna uma lista com os valores destinados ao desenho do gráfico.
    A lista contém os valores baseado no estudo computacional realizado 
    a um algoritmo.
    
    O gráfico irá mostrar:
            - previsão teórica
            - resultado experimental
            - media + erro
            - medio - erro
    '''
    def informacaoGraficoResultadoExperimental(self):
            
        calculo = Calculos()
        self.E = self.calcularEstatisticas()
        

        X = [n for n, tempo, media, var, desvioPadrao, erroAmostral in self.E]
        Y = [media for n, tempo, media, var, desvioPadrao, erroAmostral in self.E]
        
        ct=19e6

        self.LISTA_INFORMACAO_GRAFICO.append((X, Y, "", "red", "s", u"resultado experimental"))

        if(self.tipoOrd == 0 or self.tipoOrd == 1):
            Z = [calculo.complexidadeTemporalQuadratica(n)/ct for n, tempo, media, var, desvioPadrao, erroAmostral in self.E]
            pass
        else:
            Z = [calculo.complexidadeTemporalLogaritmica(n)/ct for n, tempo, media, var, desvioPadrao, erroAmostral in self.E]
            pass
        self.LISTA_INFORMACAO_GRAFICO.append((X, Z, "", "blue", "^", u"previsão teórica"))
        
        Z = [(media + erroAmostral) for n, tempo, media, var, desvioPadrao, erroAmostral in self.E]
        self.LISTA_INFORMACAO_GRAFICO.append((X, Z, "-", "green", "", u"média + erro"))
        
        
        Z = [(media - erroAmostral) for n, tempo, media, var, desvioPadrao, erroAmostral in self.E]
        self.LISTA_INFORMACAO_GRAFICO.append((X, Z, "-", "orange", "", u"média - erro"))

        return self.LISTA_INFORMACAO_GRAFICO

    def tipoOrdenacao(self):
        lista = list(self.A)
        if(self.tipoOrd == 0):
                algoritmo = InsertionSort(lista)
                algoritmo.insertion_sort()
                pass
        elif(self.tipoOrd == 1):
                algoritmo = BubbleSort(lista)
                algoritmo.bubble_sort()
                pass
        elif(self.tipoOrd == 2):
                algoritmo = Heapsort(lista)
                algoritmo.heapsort()
                pass
        elif(self.tipoOrd == 3):
                algoritmo = MergeSort(lista)
                algoritmo.merge_sort(0, len(lista)-1)
                pass
        else:
                algoritmo = Quicksort(lista)
                algoritmo.quicksort(0, len(lista)-1)
                pass
        pass
pass
